package com.lhy.tool.generator;

import com.lhy.tool.autoconfigation.GeneratorConfiguration;
import com.lhy.tool.autoconfigation.GeneratorProperties;
import com.lhy.tool.autoconfigation.GeneratorProperties.Project;
import com.lhy.tool.generator.annotation.ModelIgnoreAttribute;
import com.lhy.tool.generator.binding.GeneratorModelAttributeBinding;
import com.lhy.tool.model.CrudBean;
import com.lhy.tool.model.FileAttribute;
import freemarker.template.Template;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.nio.file.FileSystemException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

@Setter
@Getter
@Slf4j
public abstract class AbstractGenerator implements Generator {

    protected String modelName;

    @ModelIgnoreAttribute
    FileAttribute fileAttribute;

    @ModelIgnoreAttribute
    @Autowired
    GeneratorModelAttributeBinding generatorModelAttributeSetter;

    private GeneratorProperties.GlobalConfig.DocInfo docInfo;

    @Override
    public final File genCode(CrudBean crudBean, GeneratorConfiguration generatorConfiguration) {

        this.fileAttribute = this.fileAttribute(crudBean, generatorConfiguration);
        this.modelName = crudBean.getSimpleName();
        this.docInfo = generatorConfiguration.getGeneratorProperties().getGlobalConfig().getDocInfo();
        this.writeAttribute(crudBean, generatorConfiguration.getGeneratorProperties());
        File codeFile = this.getCodeFile();
        //只有所有模板文件属性加载init方法完毕，此时才知道文件路径和文件名信息，这里再设置
        this.fileAttribute.setCodeFile(codeFile);
        Map<String, Object> modelAttribute = generatorModelAttributeSetter.bingdingGeneratorModelAttribute(this, generatorConfiguration);
        this.processTemplateToFile(generatorConfiguration, modelAttribute);
        this.checkFile(codeFile);
        return codeFile;
    }

    /**
     * writeAttribute
     * <p/>
     * 给子类覆盖的属性写入方法, 给genarator成员变量设值即可
     *
     * @param crudBean
     * @param generatorProperties
     * @return void
     * @throws
     * @date 2019/1/10 16:45
     */
    protected abstract void writeAttribute(CrudBean crudBean, GeneratorProperties generatorProperties);

    /**
     * {@inheritDoc}
     *
     * @param project
     * @return
     * @Title: projectPath
     * @Description: 配置生成代码的项目路径
     */
    protected abstract String projectPath(Project project);

    /**
     * {@inheritDoc}
     *
     * @return String    返回类型
     * @Title: templateName
     * @Description: 使用的模板名称
     */
    protected abstract String templateName();

    /**
     * {@inheritDoc}
     *
     * @return File    返回类型
     * @Title: getCodeFile
     * @Description:生成的代码文件
     */
    protected abstract File getCodeFile();

    private FileAttribute fileAttribute(CrudBean beanModel, GeneratorConfiguration generatorConfiguration) {
        return FileAttribute.builder().projectPath(projectPath(generatorConfiguration.getProject()))
                .templateName(templateName()).build();
    }

    /**
     * @param generatorConfiguration
     * @param model
     * @Title: processTemplateToFile
     * @Description: 调用freemarker生成代码文件
     */
    @SneakyThrows
    private void processTemplateToFile(GeneratorConfiguration generatorConfiguration, Map<String, Object> model) {
        Set<Entry<String, Object>> entrySet = model.entrySet();
        log.debug("模板参数:");
        entrySet.forEach(entry -> log.debug("key: {},value: {}", entry.getKey(), entry.getValue()));
        Template template = generatorConfiguration.getFreeMarkerConfiguration().getTemplate(fileAttribute.getTemplateName());
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        File codeFile = fileAttribute.getCodeFile();
        FileUtils.write(codeFile, content, "utf-8");
        log.info("生成文件路径：{}", codeFile.getAbsolutePath());
        log.info("文件内容:\n"+content);
    }

    /**
     * @param file
     * @Title: checkFile
     * @Description: 检查生成的文件是否存在
     */
    @SneakyThrows
    private void checkFile(File file) {
        if (!file.exists()) {
            throw new FileNotFoundException("生成文件失败");
        }
        if (file.length() == 0) {
            throw new FileSystemException(file.getAbsolutePath(), "生成文件长度为0", null);
        }
    }

}
